package com.lfc.flowable.service.impl;

import com.lfc.flowable.domian.AjaxResult;
import com.lfc.flowable.domian.dto.ProcessDefinitionDTO;
import com.lfc.flowable.domian.dto.TaskInfo;
import com.lfc.flowable.service.ModelService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.flowable.engine.ProcessEngine;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.task.Comment;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 模型定义
 */
@Slf4j
@Service
public class ModelServiceImpl implements ModelService {

    @Resource
    private ProcessEngine processEngine;

    private static final String BPMN_FILE_SUFFIX = ".bpmn20.xml";

    /**
     * 导入模型
     *
     * @param name
     * @param category
     * @param file
     * @return
     * @throws IOException
     */
    @Override
    public AjaxResult importFile(String name, String category, MultipartFile file) throws IOException {
        InputStream in = file.getInputStream();
        Deployment deploy = processEngine.getRepositoryService().createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy();
        ProcessDefinition definition = processEngine.getRepositoryService().createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        processEngine.getRepositoryService().setProcessDefinitionCategory(definition.getId(), category);
        log.info(String.format("导入的模型id: [%s], key: [%s]", deploy.getId(), deploy.getKey()));
        return AjaxResult.success();
    }

    @Override
    public AjaxResult importXml(String name, String category, InputStream in) {
        Deployment deploy = processEngine.getRepositoryService().createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy();
        ProcessDefinition definition = processEngine.getRepositoryService().createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
        processEngine.getRepositoryService().setProcessDefinitionCategory(definition.getId(), category);
        return AjaxResult.success();
    }

    @Override
    public AjaxResult readXml(String deployId) throws IOException {
        ProcessDefinition definition = processEngine.getRepositoryService().createProcessDefinitionQuery().deploymentId(deployId).singleResult();
        InputStream inputStream = processEngine.getRepositoryService().getResourceAsStream(definition.getDeploymentId(), definition.getResourceName());
        String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name());
        return AjaxResult.success("", result);
    }

    /**
     * 获取部署bpmn的流程图
     *
     * @param processDefinitionId
     * @return
     * @throws IOException
     */
    @Override
    public byte[] getProcessDiagram(String processDefinitionId) throws IOException {
        ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery()
                .processDefinitionId(processDefinitionId)
                .singleResult();

        if (processDefinition == null || processDefinition.getDiagramResourceName() == null) {
            throw new RuntimeException("流程定义不存在或没有关联的流程图");
        }

        InputStream imageStream = processEngine.getRepositoryService().getResourceAsStream(
                processDefinition.getDeploymentId(),
                processDefinition.getDiagramResourceName());

        return IOUtils.toByteArray(imageStream);
    }

    /**
     * 获取全部流程定义列表
     *
     * @return
     */
    @Override
    public List<ProcessDefinitionDTO> getAllProcessDefinitions() {
        List<ProcessDefinition> processDefinitionList = processEngine.getRepositoryService().createProcessDefinitionQuery()
                .latestVersion()  // 关键：只返回最新版本
                .orderByProcessDefinitionKey().asc()  // 按 Key 排序
                .list();

        List<ProcessDefinitionDTO> processDefinitionDTOList = processDefinitionList.stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());


        for (ProcessDefinitionDTO pd : processDefinitionDTOList) {
            // 获取部署信息
            Deployment deployment = processEngine.getRepositoryService().createDeploymentQuery()
                    .deploymentId(pd.getDeploymentId())
                    .singleResult();
            pd.setDeploymentTime(deployment.getDeploymentTime());
        }

        return processDefinitionDTOList;
    }

    @Override
    public void delete(String[] deployIds) {
        for (String deployId : deployIds) {
            // true 允许级联删除 ,不设置会导致数据库外键关联异常
            processEngine.getRepositoryService().deleteDeployment(deployId, true);
        }
    }

    @Override
    public List<TaskInfo> history(String processInstanceId) {
        List<HistoricActivityInstance> history = processEngine.getHistoryService()
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        List<TaskInfo> infos = new ArrayList<>();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        history.stream().forEach(h -> {
            TaskInfo info = new TaskInfo();
            info.setProcessInstanceId(h.getProcessInstanceId());
            info.setTaskId(h.getTaskId());
            info.setExecutionId(h.getExecutionId());
            info.setStartTime(sdf.format(h.getStartTime()));
            if (h.getEndTime() != null) {
                info.setEndTime(sdf.format(h.getEndTime()));
            }
            //处理开始事件节点
            if ("startEvent".equals(h.getActivityType())) {
                info.setTaskName("流程发起");
                info.setAssignee(h.getAssignee());
            } else if ("userTask".equals(h.getActivityType())) {
                //用户任务处理逻辑
                info.setTaskName(h.getActivityName());
                info.setAssignee(h.getAssignee());
            } else {
                return;
            }

            List<Comment> comments = processEngine.getTaskService().getTaskComments(h.getTaskId());
            if (comments.size() > 0) {
                info.setComment(comments.get(0).getFullMessage());
            }
            infos.add(info);
        });
        return infos;
    }

    public ProcessDefinitionDTO convertToDTO(ProcessDefinition pd) {
        ProcessDefinitionDTO dto = new ProcessDefinitionDTO();
        dto.setId(pd.getId());
        dto.setKey(pd.getKey());
        dto.setName(pd.getName());
        dto.setVersion(pd.getVersion());
        dto.setDeploymentId(pd.getDeploymentId());
        dto.setResourceName(pd.getResourceName());
        dto.setSuspended(pd.isSuspended());
        dto.setCategory(pd.getCategory());
        dto.setDescription(pd.getDescription());
        return dto;
    }
}
